package com.hhy.lombok.processor;

import com.hhy.lombok.annotation.AllArgsConstructor;
import com.hhy.lombok.utils.JCUtils;
import com.hhy.lombok.utils.LoggerUtils;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.comp.Annotate;
import com.sun.tools.javac.comp.Attr;
import com.sun.tools.javac.comp.Enter;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Names;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.lang.annotation.Annotation;
import java.util.Set;

/**
 * Created with IntelliJ IDEA.
 *
 * @author hai-yang
 * @date 2022/04/20/0:51
 * @description
 */

public abstract class BaseProcessor extends AbstractProcessor {

    public static final LoggerUtils log = LoggerUtils.log;

    Messager messager;

    public JavacTrees trees;

    public TreeMaker treeMaker;

    public Names names;

    public JavacElements elementUtils;
    private Attr attr;
    private Enter enter;
    private Annotate annotate;

    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        this.messager = processingEnv.getMessager();
        this.elementUtils = (JavacElements) processingEnv.getElementUtils();
        this.treeMaker = TreeMaker.instance(context);
        this.trees = JavacTrees.instance(processingEnv);
        this.names = Names.instance(context);
        this.attr = Attr.instance(context);
        this.enter = Enter.instance(context);
        this.annotate = Annotate.instance(context);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        synchronized (this) {
            try {
                log.log("================================= start =================================");
                log.log("[" + this.getClass().getSimpleName() + "] processing .....");
                return doProcess(roundEnv);
            } catch (Throwable e) {
                log.throwableLog(e);
                throw e;
            } finally {
                log.log("[" + this.getClass().getSimpleName() + "] process end.....");
                log.log("================================= end =================================");
                log.push();
            }
        }
    }

    private boolean doProcess(RoundEnvironment roundEnv) {
        roundEnv.getElementsAnnotatedWith(getNeedToHandleAnnotation()).forEach(this::doProcess);
        return true;
    }

    protected abstract void doProcess(Element element);

    protected abstract Class<? extends Annotation> getNeedToHandleAnnotation();

    protected JCTree.JCVariableDecl createVar(String varName, String type, JCTree.JCExpression expr) {
        return JCUtils.createVar(treeMaker,
                names.fromString(varName), memberAccess(type), expr, Flags.PARAMETER);
    }

    protected JCTree.JCExpression memberAccess(String classType) {
        return JCUtils.memberAccess(treeMaker, elementUtils, classType);
    }

    protected JCTree getTree(String classPath) {
        return JCUtils.getTree(classPath, elementUtils);
    }
}
